package com.csust.automaticteach.bizmq;

import cn.hutool.core.util.StrUtil;
import com.csust.automaticteach.app.teacher.PrepareLessonApp;
import com.csust.automaticteach.constant.RabbitMQConstant;
import com.csust.automaticteach.exception.BusinessException;
import com.csust.automaticteach.exception.ErrorCode;
import com.csust.automaticteach.model.entity.GenerateTeach;
import com.csust.automaticteach.service.GenerateTeachService;
import com.rabbitmq.client.Channel;
import jakarta.annotation.Resource;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Component;

import static com.csust.automaticteach.constant.ExceptionConstant.SYSTEM_ERROR;
import static com.csust.automaticteach.constant.StatusConstant.DRAFT;
import static com.csust.automaticteach.constant.StatusConstant.RUNNING;

/**
 * 监听队列，接收消息
 */
// 使用@Component注解标记该类为一个组件，让Spring框架能够扫描并将其纳入管理
@Component
// 使用@Slf4j注解生成日志记录器
@Slf4j
public class AppMessageConsumer {
    @Resource
    private GenerateTeachService generateTeachService;
    @Resource
    private PrepareLessonApp prepareLessonApp;

    /**
     * 接收消息的方法
     *
     * @param message     接收到的消息内容，是一个字符串类型
     * @param channel     消息所在的通道，可以通过该通道与 RabbitMQ 进行交互，例如手动确认消息、拒绝消息等
     * @param deliveryTag 消息的投递标签，用于唯一标识一条消息
     */
    // 使用@SneakyThrows注解简化异常处理
    @SneakyThrows
    // 使用@RabbitListener注解指定要监听的队列名称为"code_queue"，并设置消息的确认机制为手动确认
    @RabbitListener(queues = {RabbitMQConstant.PREPARE_QUEUE_NAME}, ackMode = "MANUAL")
    // @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag是一个方法参数注解,用于从消息头中获取投递标签(deliveryTag),
    // 在RabbitMQ中,每条消息都会被分配一个唯一的投递标签，用于标识该消息在通道中的投递状态和顺序。通过使用@Header(AmqpHeaders.DELIVERY_TAG)注解,可以从消息头中提取出该投递标签,并将其赋值给long deliveryTag参数。
    public void receiveMessage(String message, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {
        // 使用日志记录器打印接收到的消息内容
        log.info("receiveMessage message = {}", message);
        if (StrUtil.isBlank(message)) {
            //如果消息为空
            //拒绝消息
            channel.basicNack(deliveryTag, false, false);
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        //从消息获取id
        Long generateTeachId = Long.parseLong(message);
        GenerateTeach generateTeach = generateTeachService.getById(generateTeachId);
        if (generateTeach == null) {
            // 如果图表为空，拒绝消息并抛出业务异常
            channel.basicNack(deliveryTag, false, false);
            throw new BusinessException(ErrorCode.NOT_FOUND_ERROR, "教学内容为空");
        }
        //构建prompt
        String context = generateTeach.getUploadContent();
        String userPrompt = generateTeachService.concatPrompt(context);
        //修改状态为执行中
        GenerateTeach updateGeTeach = new GenerateTeach();
        updateGeTeach.setId(generateTeachId);
        updateGeTeach.setStatus(RUNNING);
        boolean b = generateTeachService.updateById(updateGeTeach);
        // 如果提交失败(一般情况下,更新失败可能意味着你的数据库出问题了)
        if (!b) {
            generateTeachService.handleTeachUpdateError(generateTeachId, "更新教学内容执行中状态失败");
            throw new BusinessException(ErrorCode.OPERATION_ERROR, SYSTEM_ERROR);
        }

        //生成大纲结果 即 content
        GenerateTeach updateGeTeachResult = new GenerateTeach();
        //chatId
        String chatId = String.valueOf(System.currentTimeMillis() + generateTeachId);
        String result = prepareLessonApp.automaticPrepareLesson(userPrompt, chatId);
        updateGeTeachResult.setId(generateTeachId);//设置id
        updateGeTeachResult.setStatus(DRAFT);//设置状态为草稿
        updateGeTeachResult.setContent(result);//设置内容
        boolean updateResult = generateTeachService.updateById(updateGeTeachResult);
        if (!updateResult) {
            generateTeachService.handleTeachUpdateError(generateTeachId, "更新教学内容执行中状态失败");
        }
        // 投递标签是一个数字标识,它在消息消费者接收到消息后用于向RabbitMQ确认消息的处理状态。通过将投递标签传递给channel.basicAck(deliveryTag, false)方法,可以告知RabbitMQ该消息已经成功处理,可以进行确认和从队列中删除。
        // 手动确认消息的接收，向RabbitMQ发送确认消息
        channel.basicAck(deliveryTag, false);
    }

}
